using System.Collections.Generic;
using System.Linq;
using System.Globalization;
using System;

namespace Legend.Web.QueryString
{
    /// <summary>
    /// A collection of members that can be serialized, ordered in the
    /// order they are to be serialized.
    /// </summary>
    public class SerializableMemberInfoCollection
        : IEnumerable<SerializableMemberInfo>
    {
        private IEnumerable<SerializableMemberInfo> sortedMemberInfos;
        private Dictionary<string, SerializableMemberInfo> memberInfosByKey;

        internal SerializableMemberInfoCollection(IEnumerable<SerializableMemberInfo> memberInfos)
        {
            Require.ThatArgument(memberInfos).Named("memberInfos").IsNotNull();

            this.sortedMemberInfos = SortMemberInfos(memberInfos);
            this.memberInfosByKey = memberInfos.ToDictionary(x => x.Name, x => x);
        }

        private static SerializableMemberInfo[] SortMemberInfos(IEnumerable<SerializableMemberInfo> memberInfos)
        {
            var result = memberInfos.ToList();
            result.Sort();
            return result.ToArray();
        }

        /// <summary>
        /// Gets an enumerator that enumerates the items in the collection.
        /// </summary>
        /// <returns>An enumerator.</returns>
        public IEnumerator<SerializableMemberInfo> GetEnumerator()
        {
            return this.sortedMemberInfos.GetEnumerator();
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return this.GetEnumerator();
        }

        /// <summary>
        /// Gets the member by the specified name.
        /// </summary>
        /// <param name="memberName">The name of the contract member to get.</param>
        /// <returns>The contract member with the specified name.</returns>
        public SerializableMemberInfo this[string memberName]
        {
            get
            {
                if (!this.memberInfosByKey.ContainsKey(memberName))
                {
                    throw new KeyNotFoundException(
                        string.Format(CultureInfo.InvariantCulture,
                                      "The specified member '{0}' does not exist.",
                                      memberName));
                }

                return this.memberInfosByKey[memberName];
            }
        }

        /// <summary>
        /// Gets a value indicating if a member with the specified name exists.
        /// </summary>
        /// <param name="memberName">The name to search for.</param>
        /// <returns>True or false.</returns>
        public bool Contains(string memberName)
        {
            Require.ThatArgument(memberName).Named("memberName").IsNotNull();
            return this.memberInfosByKey.ContainsKey(memberName);
        }
    }
}